﻿#pragma once
#include "yzrutil.h"
#include "DSP.h"
#include "DSPUtil.h"
#include "RingBuffer.h"
#include "AllPassFilter.h"
#include "BufferDelayer.h"

namespace yzrilyzr_dsp{
	ECLASS(SchroederReverb, public DSP){
	private:
	RingBufferSample comb1;
	RingBufferSample comb2;
	RingBufferSample comb3;
	RingBufferSample comb4;
	u_sample deltaTime=0;
	u_sample gain1=0;
	u_sample gain2=0;
	u_sample gain3=0;
	u_sample gain4=0;
	u_sample delayIndex1=0;
	u_sample delayIndex2=0;
	u_sample delayIndex3=0;
	u_sample delayIndex4=0;
	RingBufferSample sumBuf;
	public:
	u_time delayTime1=0;
	u_time delayTime2=0;
	u_time delayTime3=0;
	u_time delayTime4=0;
	u_normal_11 feedback=1;
	u_normal_01 rt60=0;
	u_normal_01 wetRatio=0.5;
	AllPassFilter apf1;
	AllPassFilter apf2;
	BufferDelayer::DelayFunction delayFunction=BufferDelayer::linearDelay;
	public:
	SchroederReverb();
	SchroederReverb(
		u_sample delayTime1,
		u_sample delayTime2,
		u_sample delayTime3,
		u_sample delayTime4,
		u_sample feedback,
		u_sample rt60,
		u_sample apfDelay1,
		u_sample apfDelay2,
		u_sample apfGain1,
		u_sample apfGain2,
		u_normal_01 wetRatio
	) :
		delayTime1(delayTime1),
		delayTime2(delayTime2),
		delayTime3(delayTime3),
		delayTime4(delayTime4),
		feedback(feedback),
		rt60(rt60),
		wetRatio(wetRatio){
		apf1.delayTimeMs=apfDelay1;
		apf1.gain=apfGain1;
		apf2.delayTimeMs=apfDelay2;
		apf2.gain=apfGain2;
	}
	u_sample procDsp(u_sample input) override{
		if(rt60 == 0 || wetRatio == 0 || feedback == 0)return input;
		u_sample delay1=delayFunction(comb1, delayIndex1);
		u_sample delay2=delayFunction(comb2, delayIndex2);
		u_sample delay3=delayFunction(comb3, delayIndex3);
		u_sample delay4=delayFunction(comb4, delayIndex4);
		comb1.write(input + delay1 * gain1);
		comb2.write(input + delay2 * gain2);
		comb3.write(input + delay3 * gain3);
		comb4.write(input + delay4 * gain4);
		u_sample sum=(delay1 + delay2 + delay3 + delay4) / 5.0;
		sum=apf1.procDsp(sum);
		sum=apf2.procDsp(sum);
		return input * (1 - wetRatio) + sum * wetRatio;
	}
	void procBlock(u_sample * input, u_sample * output, u_index length)override{
		if(rt60 == 0 || wetRatio == 0 || feedback == 0)return;
		sumBuf.ensureCapacity(length);
		for(u_index i=0;i < length;i++){
			u_sample delay1=delayFunction(comb1, delayIndex1);
			u_sample delay2=delayFunction(comb2, delayIndex2);
			u_sample delay3=delayFunction(comb3, delayIndex3);
			u_sample delay4=delayFunction(comb4, delayIndex4);
			u_sample input1=input[i];
			comb1.write(input1 + delay1 * gain1);
			comb2.write(input1 + delay2 * gain2);
			comb3.write(input1 + delay3 * gain3);
			comb4.write(input1 + delay4 * gain4);
			u_sample sum=(delay1 + delay2 + delay3 + delay4) / 5.0;
			sumBuf.buffer[i]=sum;
		}
		apf1.procBlock(sumBuf.buffer, length);
		apf2.procBlock(sumBuf.buffer, length);
		for(u_index i=0;i < length;i++){
			output[i]=input[i] * (1 - wetRatio) + sumBuf.buffer[i] * wetRatio;
		}
	}
	void init(u_sample_rate sampleRate) override{
		this->deltaTime=sampleRate / 1000.0;
		gain1=DSPUtil::computeGainByRT60(delayTime1, rt60) * feedback;
		gain2=-DSPUtil::computeGainByRT60(delayTime2, rt60) * feedback;
		gain3=DSPUtil::computeGainByRT60(delayTime3, rt60) * feedback;
		gain4=-DSPUtil::computeGainByRT60(delayTime4, rt60) * feedback;
		apf1.init(sampleRate);
		apf2.init(sampleRate);
		delayIndex1=delayTime1 * deltaTime;
		delayIndex2=delayTime2 * deltaTime;
		delayIndex3=delayTime3 * deltaTime;
		delayIndex4=delayTime4 * deltaTime;
	}
	void resetMemory() override{
		comb1.fill(0);
		comb2.fill(0);
		comb3.fill(0);
		comb4.fill(0);
		apf1.resetMemory();
		apf2.resetMemory();
	}
	std::shared_ptr<DSP> cloneDSP() override{
		return std::make_shared < SchroederReverb>(delayTime1, delayTime2, delayTime3, delayTime4, feedback, rt60, apf1.delayTimeMs, apf2.delayTimeMs, apf1.gain, apf2.gain, wetRatio);
	}
	void cloneParam(DSP * obj1)override{
		if(auto other=dynamic_cast<SchroederReverb *>(obj1)){
			delayTime1=other->delayTime1;
			delayTime2=other->delayTime2;
			delayTime3=other->delayTime3;
			delayTime4=other->delayTime4;
			feedback=other->feedback;
			rt60=other->rt60;
			apf1.cloneParam(&other->apf1);
			apf2.cloneParam(&other->apf2);
			wetRatio=other->wetRatio;
		}
	}
	yzrilyzr_lang::String toString()const override;
	};
}